﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IterationLife.Datas;
using IterationLife.Extensions.Dtos;
using IterationLife.FootballTeaching.Enums;
using IterationLife.FootballTeaching.Models;
using IterationLife.FootballTeaching.Repositories;
using IterationLife.Mobile.AppServices.Dtos.Football;
using IterationLife.System.Repositories;
using Util.Datas;
using Util.Exceptions;

namespace IterationLife.Mobile.AppServices.Services.Football.Implements
{
    /// <summary>
    /// 足球训练服务
    /// </summary>
    public class FootballTrainService : IFootballTrainService
    {
        private readonly IUserRepository _userRepository;
        private readonly IApplicationUnitOfWork _unitOfWork;
        private readonly ICourseRepository _courseRepository;
        private readonly ICourseCategoryRepository _courseCategoryRepository;
        private readonly IChapterRepository _chapterRepository;
        private readonly ILessonRepository _lessonRepository;
        private readonly IPlayerPositionRepository _playerPositionRepository;
        private readonly IUserCourseRepository _userCourseRepository;

        private const int RecommandCourseCount = 8;
        private const int HotCourseCategoryCount = 4;
        private const int HotPositionCount = 6;
        /// <summary>
        /// 足球训练服务构造函数
        /// </summary>
        public FootballTrainService(IApplicationUnitOfWork unitOfWork,
            IUserRepository userRepository,
            ICourseRepository courseRepository,
            ICourseCategoryRepository courseCategoryRepository,
            IChapterRepository chapterRepository,
            ILessonRepository lessonRepository,
            IPlayerPositionRepository playerPositionRepository,
            IUserCourseRepository userCourseRepository)
        {
            _unitOfWork = unitOfWork;
            _userRepository = userRepository;
            _courseRepository = courseRepository;
            _courseCategoryRepository = courseCategoryRepository;
            _chapterRepository = chapterRepository;
            _lessonRepository = lessonRepository;
            _playerPositionRepository = playerPositionRepository;
            _userCourseRepository = userCourseRepository;
        }

        public CourseDetailOutput GetCourseDatail(Guid courseId, Guid? userId = null)
        {
            var course = _courseRepository.Single(d => d.Id == courseId);
            if (course == null)
                throw new Warning("课程不存在");
            bool isJoind = false;
            if (userId.HasValue)
            {
                isJoind = _userCourseRepository.Find().Any(d =>
                      d.UserId == userId.Value && d.CourseId == courseId && d.SubscribeState == (int)SubscribeState.On);
            }
            var courseOutput = new CourseDetailOutput
            {
                Id = course.Id,
                ActionNumber = course.ActionNumber,
                Banner = course.Banner.Address,
                Chapters = GetChapterOutputs(courseId),
                Difficulty = course.Difficulty.HasValue ? (int)course.Difficulty.Value : (int)DifficultyType.OneLevel,
                Name = course.Name,
                ParticipationNumber = course.ParticipationNumber,
                IsJoined = isJoind
            };

            return courseOutput;
        }

        private List<ChapterOutput> GetChapterOutputs(Guid courseId)
        {
            var chapterQuery = _chapterRepository.Find().Where(d => d.CourseId == courseId);
            var chapters = chapterQuery.ToList();
            var lessions = _lessonRepository.Find().Where(d => chapterQuery.Any(x => x.Id == d.ChapterId)).ToList();

            var chapterOutputs = chapters.Select(chapter => new ChapterOutput
            {
                Id = chapter.Id,
                CourseId = courseId,
                Name = chapter.Name,
                Lessons = lessions.Where(lesson => lesson.ChapterId == chapter.Id).Select(lesson => new LessonOutput
                {
                    Id = lesson.Id,
                    Banner = lesson.Banner.Address,
                    Name = lesson.Name,
                }).ToList(),
            }).ToList();
            return chapterOutputs;
        }

        public LessonDetailOutput GetLessonDetail(Guid lessionId)
        {
            var lession = _lessonRepository.Single(d => d.Id == lessionId);
            if (lession == null)
                throw new Warning("课时不存在");
            var lessons = _lessonRepository.Find().Where(d => d.ChapterId == lession.ChapterId).ToList();
            var currentLessonSort = lession.SortId;
            var previousLesson = lessons.SingleOrDefault(d => d.SortId == currentLessonSort - 1);
            var nextLesson = lessons.SingleOrDefault(d => d.SortId == currentLessonSort + 1);

            return new LessonDetailOutput
            {
                Id = lession.Id,
                Content = lession.Content,
                Difficulty = lession.Difficulty.HasValue ? (int)lession.Difficulty.Value : (int)DifficultyType.OneLevel,
                Name = lession.Name,
                Requirement = lession.Requirement,
                Banner = lession.Banner.Address,
                Video = lession.Video.Address,
                PreviousId = previousLesson?.Id,
                NextId = nextLesson?.Id,
                TotalCount = lessons.Count,
                Sort = lession.SortId
            };
        }

        public List<CourseOutput> GetRecommendedCourses(Guid userId)
        {
            return _courseRepository.Find()              
                .OrderByDescending(d => d.SortId)
                .Take(RecommandCourseCount).ToList()
                .Select(d => new CourseOutput
                {
                    Id = d.Id,
                    Name = d.Name,
                    Banner = d.Banner.Address,
                    ActionNumber = d.ActionNumber,
                    Difficulty = d.Difficulty.HasValue ? (int)d.Difficulty.Value : (int)DifficultyType.OneLevel,
                    ParticipationNumber = d.ParticipationNumber,
                }).ToList();
        }

        public List<CourseCategoryOutput> GetHotCourseCategories()
        {
            return _courseCategoryRepository.Find()
                .Where(d=>d.Enabled)
                .OrderByDescending(d => d.SortId)
                .Take(HotCourseCategoryCount).ToList()
                .Select(d => new CourseCategoryOutput
                {
                    Id = d.Id,
                    Name = d.Name,
                    Banner = d.Banner.Address,
                }).ToList();
        }

        public List<PlayerPositionOutput> GetHotPositions()
        {
            return _playerPositionRepository.Find()
                .Where(d=>d.Enabled)
                .OrderByDescending(d => d.SortId)
                .Take(HotPositionCount)
                .Select(d => new PlayerPositionOutput
                {
                    Id = d.Id,
                    Name = d.Name,
                    Code = d.Code,
                }).ToList();
        }


        public PagedData<CourseOutput> GetPagedCourses(int page, int size, Guid? userId = null, Guid? courseCategoryId = null, string positionCode = null)
        {
            var courseQuery = _courseRepository.Find();
            courseQuery = FilterCourseByCategoryId(courseCategoryId, courseQuery);
            courseQuery = courseQuery.WhereIf(d => d.PlayerPositionsJsonStr.Contains(positionCode),
                !string.IsNullOrWhiteSpace(positionCode));

            IEnumerable<Guid> userCourseIds = userId.HasValue ? GetUserCourses(userId.Value).Select(d => d.Id) : new List<Guid>();
            var totalCount = courseQuery.Count();
            int pageIndex = page > 0 ? page - 1 : 0;
            var pagedDatas = courseQuery
                .OrderByDescending(d => d.SortId)
                .Skip(pageIndex * size).Take(size)
                .ToList()
                .Select(d => new CourseOutput
                {
                    ActionNumber = d.ActionNumber,
                    Banner = d.Banner.Address,
                    Difficulty = d.Difficulty.HasValue ? (int)d.Difficulty.Value : (int)DifficultyType.OneLevel,
                    Name = d.Name,
                    Id = d.Id,
                    ParticipationNumber = d.ParticipationNumber,
                    IsJoined = userCourseIds.Contains(d.Id),
                }).ToList();

            return new PagedData<CourseOutput>(pagedDatas, page, size, totalCount);
        }

        private static IQueryable<Course> FilterCourseByCategoryId(Guid? courseCategoryId, IQueryable<Course> courseQuery)
        {
            if (courseCategoryId.HasValue)
            {
                var courseCategoryIdStr = courseCategoryId.Value.ToString();
                courseQuery = courseQuery.Where(d => d.CourseCategoriesJsonStr.Contains(courseCategoryIdStr));
            }
            return courseQuery;
        }


        public List<CourseOutput> GetUserCourses(Guid userId)
        {
            var onState = (int)SubscribeState.On;
            var userCourseIdQuery = _userCourseRepository.Find().Where(d => d.UserId == userId && d.SubscribeState == onState).Select(d => d.CourseId);
            var courseOutputs = _courseRepository.Find().Where(d => userCourseIdQuery.Contains(d.Id)).ToList()
                .Select(d => new CourseOutput
                {
                    Id = d.Id,
                    Difficulty = d.Difficulty.HasValue ? (int)d.Difficulty.Value : (int)DifficultyType.OneLevel,
                    ActionNumber = d.ActionNumber,
                    Banner = d.Banner.Address,
                    Name = d.Name,
                    ParticipationNumber = d.ParticipationNumber,
                    IsJoined = true,
                }).ToList();
            return courseOutputs;
        }

        public void JoinToUserCourses(Guid userId, Guid courseId)
        {
            var user = _userRepository.Single(d => d.Id == userId);
            if (user == null)
                throw new Warning("用户不存在");

            var userCourse = _userCourseRepository.Single(d => d.UserId == userId && d.CourseId == courseId);
            if (userCourse != null)
            {
                userCourse.SubscribeState = (int)SubscribeState.On;
            }
            else
            {
                userCourse = new UserCourse(Guid.NewGuid())
                {
                    UserId = userId,
                    CourseId = courseId,
                    CreationTime = DateTime.Now,
                    SubscribeState = (int)SubscribeState.On,
                };
                _userCourseRepository.Add(userCourse);
            }
            _unitOfWork.Commit();
        }

        public void RemoveFromUserCourses(Guid userId, Guid courseId)
        {
            var user = _userRepository.Single(d => d.Id == userId);
            if (user == null)
                throw new Warning("用户不存在");

            var userCourse = _userCourseRepository.Single(d => d.UserId == userId && d.CourseId == courseId);
            if (userCourse != null)
            {
                userCourse.SubscribeState = (int)SubscribeState.Off;
                _unitOfWork.Commit();
            }
        }

        public List<PlayerPositionOutput> GetAllPlayerPositions()
        {
            var playerPositions = _playerPositionRepository.Find()
                .Where(d=>d.Enabled)
                .OrderByDescending(d => d.SortId)
                .Select(d => new PlayerPositionOutput
                {
                    Id = d.Id,
                    Name = d.Name,
                    Code = d.Code
                }).ToList();
            return playerPositions;
        }
    }
}
